/* Copyright (C) 1999 Masanao Izumo <iz@onicos.co.jp>
 *               2005-2007 Ma Bingyao <andot@ujn.edu.cn>
 * Version: 2.4
 * LastModified: Oct 19, 2007
 * This library is free.  You can redistribute it and/or modify it.
 * @package js.codec
 * @author: DSONet (dsonet@msn.com)
 * @version $Id: UTF8.js 97 2009-12-18 09:58:31Z dsonet@gmail.com $
 * improve the performance for ++ operator
 * for further information, see http://u8u16.costar.sfu.ca/
 */
/*
 * UTF-8 <=> UTF-16 convertion library.
 * Interfaces:
 * var utf8, utf16;
 * utf16 = "你好中国";
 * utf8 = UTF8.encode(utf16);
 * utf16 = UTF8.decode(utf8);
 */
/*var UTF8 = {
	/** 
	 * 将字符串编码为utf8 
	 * @public
	 * @param {string} str 要编码的字符串
	 * @return {string} utf8格式的字符串
	 * @id UTF8.encode
	 */
/*	encode : function(str) {
		if (str.match(CHAR_EXT_RE) != null) {
			return str;
		}
		var out = [], i = 0, j = 0, len = str.length, c, c2;
		for (; i < len; ++i, ++j) {
			c = str.charCodeAt(i);
			if (c <= 0x7f) {//127
				out[j] = str.charAt(i);
			}
			else if (c <= 0x7ff) {//2047
				out[j] = String.fromCharCode(0xc0 | (c >>> 6),
											 0x80 | (c & 0x3f));
			}
			else if (c < 0xd800 || c > 0xdfff) {
				out[j] = String.fromCharCode(0xe0 | (c >>> 12),
											 0x80 | ((c >>> 6) & 0x3f),
											 0x80 | (c & 0x3f));
			}
			else {
				if (++i < len) {
					c2 = str.charCodeAt(i);
					if (c <= 0xdbff && 0xdc00 <= c2 && c2 <= 0xdfff) {
						c = ((c & 0x03ff) << 10 | (c2 & 0x03ff)) + 0x010000;
						if (0x010000 <= c && c <= 0x10ffff) {
							out[j] = String.fromCharCode(0xf0 | ((c >>> 18) & 0x3f),
														 0x80 | ((c >>> 12) & 0x3f),
														 0x80 | ((c >>> 6) & 0x3f),
														 0x80 | (c & 0x3f));
						}
						else {
						   out[j] = '?';
						}
					}
					else {
						--i;
						out[j] = '?';
					}
				}
				else {
					--i;
					out[j] = '?';
				}
			}
		}
		return out.join('');
	}
	/** 
	 * 将字符串解码为utf16格式 
	 * @public
	 * @param {string} str 要解码的字符串
	 * @return {string} utf16格式的字符串
	 * @id UTF8.decode
	 */
/*	, decode : function(str) {

		if ((str.match(CHAR_EXT_RE) != null) ||
			(str.match(CHAR_OUT_EXT_RE) == null)) {
			return str;
		}
		var out = [], i = -1, j = -1, len = str.length, c, c2, c3, c4, s;
		while (++i < len) {
			c = str.charCodeAt(i);
			switch (c >> 4) { 
				case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
				// 0xxx xxxx
				out[++j] = str.charAt(i);
				break;
				case 12: case 13:
				// 110x xxxx   10xx xxxx
				c2 = str.charCodeAt(++i);
				out[++j] = String.fromCharCode(((c  & 0x1f) << 6) |
												(c2 & 0x3f));
				break;
				case 14:
				// 1110 xxxx  10xx xxxx  10xx xxxx
				c2 = str.charCodeAt(++i);
				c3 = str.charCodeAt(++i);
				out[++j] = String.fromCharCode(((c  & 0x0f) << 12) |
											   ((c2 & 0x3f) <<  6) |
												(c3 & 0x3f));
				break;
				case 15:
				switch (c & 0xf) {
					case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
					// 1111 0xxx  10xx xxxx  10xx xxxx  10xx xxxx
					c2 = str.charCodeAt(++i);
					c3 = str.charCodeAt(++i);
					c4 = str.charCodeAt(++i);
					s = ((c  & 0x07) << 18) |
						((c2 & 0x3f) << 12) |
						((c3 & 0x3f) <<  6) |
						 (c4 & 0x3f) - 0x10000;
					if (0 <= s && s <= 0xfffff) {
						out[++j] = String.fromCharCode(((s >>> 10) & 0x03ff) | 0xd800,
													  (s         & 0x03ff) | 0xdc00);
					}
					else {
						out[++j] = '?';
					}
					break;
					case 8: case 9: case 10: case 11:
					// 1111 10xx  10xx xxxx  10xx xxxx  10xx xxxx  10xx xxxx
					i += 4;
					out[++j] = '?';
					break;
					case 12: case 13:
					// 1111 110x  10xx xxxx  10xx xxxx  10xx xxxx  10xx xxxx  10xx xxxx
					i += 5;
					out[++j] = '?';
					break;
				}
			}
		}
		return out.join('');
	}
};
var CHAR_EXT_RE = /^[\x00-\x7f]*$/;
var CHAR_OUT_EXT_RE = /^[\x00-\xff]*$/;*/
/*
Fast incremental JavaScript UTF-8 encoder/decoder, by Jacob Rus.

API for decode from Orbited: as far as I know, the first incremental
JavaScript UTF-8 decoder.

Inspired by the observation by Johan Sundström published at:
http://ecmanaut.blogspot.com/2006/07/encoding-decoding-utf8-in-javascript.html

Note that this code throws an error for invalid UTF-8. Because it is so much
faster than previous implementations, the recommended way to do lenient
parsing is to first try this decoder, and then fall back on a slower lenient
decoder if necessary for the particular use case.

--------------------

Copyright (c) 2009 Jacob Rus

Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
*/
var UTF8 = {
	encode: function (unicode_string) {
		// Unicode encoder: Given an arbitrary unicode string, returns a string
		// of characters with code points in range 0x00 - 0xFF corresponding to
		// the bytes of the utf-8 representation of those characters.
		try {
			return unescape(encodeURIComponent(unicode_string));
		}
		catch (ex) {
			throw 'UTF8.encode:invalid input string';
		};
	}
	, decode: function (bytes) {
		// Unicode decoder: Given a string of characters with code points in
		// range 0x00 - 0xFF, which, when interpreted as bytes, are valid UTF-8,
		// returns the corresponding Unicode string, along with the number of
		// bytes in the input string which were successfully parsed.
		//
		// Unlike most JavaScript utf-8 encode/decode implementations, properly
		// deals with partial multi-byte characters at the end of the byte string.
		if (/[^\x00-\xFF]/.test(bytes)) {
			throw 'UTF8.decode:invalid utf-8 bytes';
		};
		var len = bytes.length, last = len - 1,  len_parsed = len;
		// test for non-ascii final byte. if last byte is ascii (00-7F) we're done.
		if (bytes.charCodeAt(last) >= 0x80) {
			// loop through last 3 bytes looking for first initial byte of unicode
			// multi-byte character. If the initial byte is 4th from the end, we'll
			// parse the whole string.
			for (var i = 1; i <= 3; i++) {
				// initial bytes are in range C0-FF
				if (bytes.charCodeAt(len - i) >= 0xC0) {
					len_parsed = len - i;
					break;
				};
			};
			try {
				// if the last few bytes are a complete multi-byte character, parse
				// everything (by setting len_parsed)
				decodeURIComponent(escape(bytes.slice(len_parsed)));
				len_parsed = len;
			}
			catch (ex) { /* pass */ };
		};
		//UTF8.parsedLength = len_parsed;
		try {
			return decodeURIComponent(escape(bytes.slice(0, len_parsed)));
		}
		catch (ex) {
			throw 'UTF8.decode:invalid utf-8 bytes';
		};
	}
};